; ModuleID = 'parallel_dot.ll'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32"
target triple = "i386-pc-linux-gnu"

%struct.MPI_Status = type { i32, i32, i32, i32, i32 }
%struct.ompi_communicator_t = type opaque
%struct.ompi_datatype_t = type opaque
%struct.ompi_op_t = type opaque
%struct.ompi_predefined_communicator_t = type opaque
%struct.ompi_predefined_datatype_t = type opaque
%struct.ompi_predefined_op_t = type opaque

@ompi_mpi_comm_world = external global %struct.ompi_predefined_communicator_t
@.str = private constant [31 x i8] c"Enter the order of the vectors\00", align 4
@.str1 = private constant [3 x i8] c"%d\00", align 1
@ompi_mpi_int = external global %struct.ompi_predefined_datatype_t
@.str2 = private constant [17 x i8] c"the first vector\00", align 1
@.str3 = private constant [18 x i8] c"the second vector\00", align 1
@.str4 = private constant [23 x i8] c"The dot product is %f\0A\00", align 1
@.str5 = private constant [10 x i8] c"Enter %s\0A\00", align 1
@.str6 = private constant [3 x i8] c"%f\00", align 1
@ompi_mpi_float = external global %struct.ompi_predefined_datatype_t
@ompi_mpi_op_sum = external global %struct.ompi_predefined_op_t

define i32 @main(i32 %argc, i8** %argv) nounwind {
entry:
  %argc_addr = alloca i32, align 4
  %argv_addr = alloca i8**, align 4
  %local_x = alloca [100 x float]
  %local_y = alloca [100 x float]
  %n = alloca i32
  %p = alloca i32
  %my_rank = alloca i32
  %"alloca point" = bitcast i32 0 to i32
  store i32 %argc, i32* %argc_addr
  store i8** %argv, i8*** %argv_addr
  %0 = call i32 @MPI_Init(i32* %argc_addr, i8*** %argv_addr) nounwind
  %1 = call i32 @MPI_Comm_size(%struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*), i32* %p) nounwind
  %2 = call i32 @MPI_Comm_rank(%struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*), i32* %my_rank) nounwind
  %3 = load i32* %my_rank, align 4
  %4 = icmp eq i32 %3, 0
  br i1 %4, label %bb, label %bb1

bb:                                               ; preds = %entry
  %5 = call i32 @puts(i8* getelementptr inbounds ([31 x i8]* @.str, i32 0, i32 0)) nounwind
  %6 = call i32 (i8*, ...)* @"\01__isoc99_scanf"(i8* noalias getelementptr inbounds ([3 x i8]* @.str1, i32 0, i32 0), i32* %n) nounwind
  br label %bb1

bb1:                                              ; preds = %bb, %entry
  %n2 = bitcast i32* %n to i8*
  %7 = call i32 @MPI_Bcast(i8* %n2, i32 1, %struct.ompi_datatype_t* bitcast (%struct.ompi_predefined_datatype_t* @ompi_mpi_int to %struct.ompi_datatype_t*), i32 0, %struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*)) nounwind
  %8 = load i32* %n, align 4
  %9 = load i32* %p, align 4
  %10 = sdiv i32 %8, %9
  %11 = load i32* %my_rank, align 4
  %12 = load i32* %p, align 4
  %local_x3 = bitcast [100 x float]* %local_x to float*
  call void @Read_vector(i8* getelementptr inbounds ([17 x i8]* @.str2, i32 0, i32 0), float* %local_x3, i32 %10, i32 %12, i32 %11) nounwind
  %13 = load i32* %my_rank, align 4
  %14 = load i32* %p, align 4
  %local_y4 = bitcast [100 x float]* %local_y to float*
  call void @Read_vector(i8* getelementptr inbounds ([18 x i8]* @.str3, i32 0, i32 0), float* %local_y4, i32 %10, i32 %14, i32 %13) nounwind
  %local_x5 = bitcast [100 x float]* %local_x to float*
  %local_y6 = bitcast [100 x float]* %local_y to float*
  %15 = call float @Parallel_dot(float* %local_x5, float* %local_y6, i32 %10) nounwind
  %16 = load i32* %my_rank, align 4
  %17 = icmp eq i32 %16, 0
  br i1 %17, label %bb7, label %bb8

bb7:                                              ; preds = %bb1
  %18 = fpext float %15 to double
  %19 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([23 x i8]* @.str4, i32 0, i32 0), double %18) nounwind
  br label %bb8

bb8:                                              ; preds = %bb7, %bb1
  %20 = call i32 @MPI_Finalize() nounwind
  br label %return

return:                                           ; preds = %bb8
  ret i32 undef
}

declare i32 @MPI_Init(i32*, i8***)

declare i32 @MPI_Comm_size(%struct.ompi_communicator_t*, i32*)

declare i32 @MPI_Comm_rank(%struct.ompi_communicator_t*, i32*)

declare i32 @puts(i8*)

declare i32 @"\01__isoc99_scanf"(i8* noalias, ...) nounwind

declare i32 @MPI_Bcast(i8*, i32, %struct.ompi_datatype_t*, i32, %struct.ompi_communicator_t*)

declare i32 @printf(i8* noalias, ...) nounwind

declare i32 @MPI_Finalize()

define void @Read_vector(i8* %prompt, float* %local_v, i32 %n_bar, i32 %p, i32 %my_rank) nounwind {
entry:
  %temp = alloca [100 x float]
  %status = alloca %struct.MPI_Status
  %"alloca point" = bitcast i32 0 to i32
  %0 = icmp eq i32 %my_rank, 0
  br i1 %0, label %bb, label %bb12

bb:                                               ; preds = %entry
  %1 = call i32 (i8*, ...)* @printf(i8* noalias getelementptr inbounds ([10 x i8]* @.str5, i32 0, i32 0), i8* %prompt) nounwind
  br label %bb2

bb1:                                              ; preds = %bb2
  %2 = getelementptr inbounds float* %local_v, i32 %i.0
  %3 = call i32 (i8*, ...)* @"\01__isoc99_scanf"(i8* noalias getelementptr inbounds ([3 x i8]* @.str6, i32 0, i32 0), float* %2) nounwind
  %4 = add nsw i32 %i.0, 1
  br label %bb2

bb2:                                              ; preds = %bb1, %bb
  %i.0 = phi i32 [ 0, %bb ], [ %4, %bb1 ]
  %5 = icmp slt i32 %i.0, %n_bar
  br i1 %5, label %bb1, label %bb3

bb3:                                              ; preds = %bb2
  br label %bb10

bb4:                                              ; preds = %bb10
  br label %bb6

bb5:                                              ; preds = %bb6
  %6 = getelementptr inbounds [100 x float]* %temp, i32 0, i32 %i.1
  %7 = call i32 (i8*, ...)* @"\01__isoc99_scanf"(i8* noalias getelementptr inbounds ([3 x i8]* @.str6, i32 0, i32 0), float* %6) nounwind
  %8 = add nsw i32 %i.1, 1
  br label %bb6

bb6:                                              ; preds = %bb5, %bb4
  %i.1 = phi i32 [ 0, %bb4 ], [ %8, %bb5 ]
  %9 = icmp slt i32 %i.1, %n_bar
  br i1 %9, label %bb5, label %bb7

bb7:                                              ; preds = %bb6
  %temp8 = bitcast [100 x float]* %temp to float*
  %temp89 = bitcast float* %temp8 to i8*
  %10 = call i32 @MPI_Send(i8* %temp89, i32 %n_bar, %struct.ompi_datatype_t* bitcast (%struct.ompi_predefined_datatype_t* @ompi_mpi_float to %struct.ompi_datatype_t*), i32 %q.0, i32 0, %struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*)) nounwind
  %11 = add nsw i32 %q.0, 1
  br label %bb10

bb10:                                             ; preds = %bb7, %bb3
  %q.0 = phi i32 [ 1, %bb3 ], [ %11, %bb7 ]
  %12 = icmp slt i32 %q.0, %p
  br i1 %12, label %bb4, label %bb11

bb11:                                             ; preds = %bb10
  br label %bb13

bb12:                                             ; preds = %entry
  %13 = bitcast float* %local_v to i8*
  %14 = call i32 @MPI_Recv(i8* %13, i32 %n_bar, %struct.ompi_datatype_t* bitcast (%struct.ompi_predefined_datatype_t* @ompi_mpi_float to %struct.ompi_datatype_t*), i32 0, i32 0, %struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*), %struct.MPI_Status* %status) nounwind
  br label %bb13

bb13:                                             ; preds = %bb12, %bb11
  br label %return

return:                                           ; preds = %bb13
  ret void
}

declare i32 @MPI_Send(i8*, i32, %struct.ompi_datatype_t*, i32, i32, %struct.ompi_communicator_t*)

declare i32 @MPI_Recv(i8*, i32, %struct.ompi_datatype_t*, i32, i32, %struct.ompi_communicator_t*, %struct.MPI_Status*)

define float @Serial_dot(float* %x, float* %y, i32 %n) nounwind {
entry:
  %"alloca point" = bitcast i32 0 to i32
  br label %bb1

bb:                                               ; preds = %bb1
  %0 = getelementptr inbounds float* %x, i32 %i.0
  %1 = load float* %0, align 1
  %2 = getelementptr inbounds float* %y, i32 %i.0
  %3 = load float* %2, align 1
  %4 = fmul float %1, %3
  %5 = fadd float %4, %sum.0
  %6 = add nsw i32 %i.0, 1
  br label %bb1

bb1:                                              ; preds = %bb, %entry
  %i.0 = phi i32 [ 0, %entry ], [ %6, %bb ]
  %sum.0 = phi float [ 0.000000e+00, %entry ], [ %5, %bb ]
  %7 = icmp slt i32 %i.0, %n
  br i1 %7, label %bb, label %bb2

bb2:                                              ; preds = %bb1
  br label %return

return:                                           ; preds = %bb2
  ret float %sum.0
}

define float @Parallel_dot(float* %local_x, float* %local_y, i32 %n_bar) nounwind {
entry:
  %local_dot = alloca float
  %dot = alloca float
  %"alloca point" = bitcast i32 0 to i32
  store float 0.000000e+00, float* %dot, align 4
  %0 = call float @Serial_dot(float* %local_x, float* %local_y, i32 %n_bar) nounwind
  store float %0, float* %local_dot, align 4
  %local_dot1 = bitcast float* %local_dot to i8*
  %dot2 = bitcast float* %dot to i8*
  %1 = call i32 @MPI_Reduce(i8* %local_dot1, i8* %dot2, i32 1, %struct.ompi_datatype_t* bitcast (%struct.ompi_predefined_datatype_t* @ompi_mpi_float to %struct.ompi_datatype_t*), %struct.ompi_op_t* bitcast (%struct.ompi_predefined_op_t* @ompi_mpi_op_sum to %struct.ompi_op_t*), i32 0, %struct.ompi_communicator_t* bitcast (%struct.ompi_predefined_communicator_t* @ompi_mpi_comm_world to %struct.ompi_communicator_t*)) nounwind
  %2 = load float* %dot, align 4
  br label %return

return:                                           ; preds = %entry
  ret float %2
}

declare i32 @MPI_Reduce(i8*, i8*, i32, %struct.ompi_datatype_t*, %struct.ompi_op_t*, i32, %struct.ompi_communicator_t*)
